package com.haiwei.poi.word.manager;

import com.alibaba.fastjson.JSONObject;
import com.haiwei.poi.common.HaiweiStringUtil;
import com.haiwei.poi.word.common.HaiweiDocumentGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigInteger;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * word 生成器.
 *
 * @date: 2022-06-24
 * @version: 1.0
 * @author: karry@haiwei.com
 */
@Slf4j
public class HaiweiWordExporteUtil1 {
    
    protected Pattern mergePattern = Pattern.compile("(#{1,}|##=|##={0,1}\\{[^}]{0,}?)$");
    
    protected Pattern pattern = Pattern.compile("((##|##=)\\{(.+?)\\})");
    
    protected static String KW_SERIAL_NUMBER = "fun_serialNum";
    
    protected List<File> files;
    
    private Map<BigInteger,BigInteger> relationMap = new HashMap();
    private Map<BigInteger,String> commentMap = new HashMap();
    
    public static void main(String[] args) {
        try {
            final XWPFDocument build = HaiweiDocumentGenerator.build(new URL("https://devrs.s3.cn-north-1.amazonaws.com.cn/templatePrint/2021/06/08/84d123f3-50bd-4499-97ca-6d7b43f4eacb.docx"));
    
    
            final File file = HaiweiDocumentGenerator.toFile(build);
    
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
    public XWPFDocument exportWord07(XWPFDocument template, Map<String,Object> modelMap){
        
        // 预处理占位符换行，处于多XWPFRun的场景
        this.preHandleParagraphs(template);
        this.preHandleTable(template);
    
        this.parseParagraphs(template,null,null);
        return null;
    }
    
    private void parseParagraphs(XWPFDocument xwpfDocument,Map<String,TemplateDataResult> data, Integer rowNum){
        List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
        this.parseParagraphs(xwpfDocument,paragraphs, data,rowNum,false);
    }
    
    private void parseParagraphs(XWPFDocument xwpfDocument,List<XWPFParagraph> paragraphs, Map<String,TemplateDataResult> data,Integer dataNo, boolean isDetail) {
        for(XWPFParagraph paragraph : paragraphs){
            this.parseTextBoxContentUseXPath(paragraph,data,isDetail);
            List<XWPFRun> runs = paragraph.getRuns();
            for(XWPFRun run : runs){
                String text = run.getText(0);
                if(!HaiweiStringUtil.isBlank(text)){
                    List<File> imageFiles = new ArrayList<>();
                    String parseText = this.parseMatch(text,data,dataNo,isDetail,imageFiles);
                    //填充内容 主要区分有么有换行符的场景
                    fillXWPFRunContent(run, parseText);
                    if( imageFiles.size() > 0){
                        addPictures(xwpfDocument,paragraph,run,imageFiles);
                    }
                }
            }
            if(isDetail){
                paragraph.setNumID(null);
            }
        }
    }
    
    private String parseComments(XWPFDocument xwpfDocument,XWPFParagraph paragraph){
        String comment = null;
        CTMarkupRange[] ctMarkupRanges = paragraph.getCTP().getCommentRangeStartArray();
        if(ctMarkupRanges.length > 0){
            CTMarkupRange ctMarkupRange = ctMarkupRanges[0];
            BigInteger markId = ctMarkupRange.getId();
            XWPFComment xwpfComment = xwpfDocument.getCommentByID(String.valueOf(markId));
            comment = xwpfComment.getText();
            paragraph.getCTP().removeCommentRangeStart(0);
        }
        
        return comment;
    }
    
    private void addPictures(XWPFDocument xwpfDocument, XWPFParagraph paragraph, XWPFRun run ,List<File> imageFiles){
        Iterator<File> it = imageFiles.iterator();
        String comment = this.parseComments(xwpfDocument, paragraph);
        if(HaiweiStringUtil.isBlank(comment)){
            comment = commentMap.get(relationMap.get(paragraph.getNumID()));
            if(HaiweiStringUtil.isBlank(comment)){
                comment = commentMap.get(paragraph.getNumID());
            }
        }
        int width = 0, height = 0;
        if(HaiweiStringUtil.isNotBlank(comment) && isJson(comment)){
            JSONObject feature = JSONObject.parseObject(comment);
            width = feature.getInteger("width");
            height = feature.getIntValue("height");
        }
        while(it.hasNext()){
            File file = it.next();
            try(InputStream inputStream = new FileInputStream(file)){
                if(width == 0 || height ==0){
                    BufferedImage image = ImageIO.read(file);
                    width = image.getWidth();
                    height = image.getHeight();
                }
                run.addPicture(inputStream,Document.PICTURE_TYPE_PNG,file.getName(), Units.toEMU(width),Units.toEMU(height));
            } catch (FileNotFoundException e) {
                log.error("padding print template data->failed to parse the template, image file not found. {%s}",e.getMessage());
            } catch (IOException e) {
                log.error("padding print template data->failed to parse the template, FileIO exception. {%s}",e.getMessage());
            } catch (InvalidFormatException e) {
                log.error("padding print template data->failed to parse the template, Invalid image format. {%s}",e.getMessage());
            }finally {
                it.remove();
            }
        }
    }
    
    private boolean isJson(String str){
        try {
            JSONObject.parseObject(str);
            return  true;
        } catch (Exception e) {
            return false;
        }
    }
    
    private void fillXWPFRunContent(XWPFRun run, String parseText) {
        if (parseText.contains("\n")) {
            String[] arrayContent = parseText.split("\n");
            for (int i = 0; i < arrayContent.length; i++) {
                String content = arrayContent[i];
                if (i == 0) {
                    run.setText(content, 0);
                    run.addBreak(BreakType.TEXT_WRAPPING);
                    continue;
                } else if (i != arrayContent.length - 1) {
                    run.setText(content);
                    run.addBreak(BreakType.TEXT_WRAPPING);
                } else {
                    run.setText(content);
                }
            }
        } else {
            run.setText(parseText, 0);
        }
    }
    
    private String parseCellContent(Matcher matcher, boolean isDetail) {
        String key = matcher.group(3);//注意正则分组
        String dataKey = key;
        if (isDetail) {
            if (key.contains(".")) {
                dataKey = key.split("\\.")[1];
            }
        }
        return dataKey;
    }
    
    private String parseMatch(String content,Map<String,TemplateDataResult> data,Integer dataNo, boolean isDetail,List<File> imageFiles){
        Matcher matcher = pattern.matcher(content);
        while(matcher.find()){
            String dataKey = parseCellContent(matcher, isDetail);
            if (KW_SERIAL_NUMBER.equals(dataKey)) {
                content = String.valueOf(dataNo);
            }else {
                if (null == dataKey || !data.keySet().contains(dataKey)) {
                    continue;
                }
                TemplateDataResult result = data.get(dataKey);
                String temp = (null == result.getData()) ? " " : result.getData().toString();
                if(imageFiles!=null && result.isImage() && HaiweiStringUtil.isNotBlank(temp)){
                    String imageUrl = temp.split("\n")[0];
                    File file = null;
                    try {
                        file = ImageUtil.convert(FileUtil.toLocal(imageUrl), "png");
                    } catch (Exception e) {
                        log.error("padding print template data->failed to parse the template, convert image error.{%s}",e.getMessage());
                    }
                    //统一保存起来，最后统一清理。插入的图片时，只能在write后才能被删，否则会报文件不存在
                    files.add(file);
                    imageFiles.add(file);
                }
                content = result.isImage() ? "" : content.replace(matcher.group(1),temp);
            }
            
        }
        return content;
    }
    
    
    /**
     * 处理文本框
     * @param paragraph
     * @param data
     * @param isDetail
     */
    private void parseTextBoxContentUseXPath(XWPFParagraph paragraph, Map<String,TemplateDataResult> data, boolean isDetail) {
        String nameSpace = "declare namespace o='urn:schemas-microsoft-com:office:office'; " +
                "declare namespace r='http://schemas.openxmlformats.org/officeDocument/2006/relationships'; " +
                "declare namespace mc='http://schemas.openxmlformats.org/markup-compatibility/2006'; " +
                "declare namespace m='http://schemas.openxmlformats.org/officeDocument/2006/math'; " +
                "declare namespace v='urn:schemas-microsoft-com:vml'; " +
                "declare namespace wp='http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing'; " +
                "declare namespace w10='urn:schemas-microsoft-com:office:word'; " +
                "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main'; " +
                "declare namespace wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape';" +
                "declare namespace wne='http://schemas.microsoft.com/office/word/2006/wordml';";
        String xPath = "$this//wps:txbx/w:txbxContent/w:p/w:r/w:t";
        
        CTR[] ctr = paragraph.getCTP().getRArray();
        if(ctr.length == 0) {
            return;
        }
        XmlObject object = ctr[0];
        XmlCursor selectPathCursor = getXmlObjectByXPath(object,nameSpace,xPath);
        int count = selectPathCursor.getSelectionCount();
        if(count ==0 ) {
            return;
        }
        StringBuilder txbxBuilder = new StringBuilder();
        selectPathCursor.push();
        while (selectPathCursor.toNextSelection()) {
            String text = selectPathCursor.getTextValue();
            txbxBuilder.append(text);
            selectPathCursor.setTextValue("");
        }
        selectPathCursor = getXmlObjectByXPath(object,nameSpace,xPath);
        if(selectPathCursor.hasNextSelection()){
            selectPathCursor.toNextSelection();
            String text = parseMatch(txbxBuilder.toString(), data,null, isDetail,null);
            selectPathCursor.setTextValue(text);
            
        }
        selectPathCursor.dispose();
    }
    
    /**
     * 获取xml游标
     * @param xml
     * @param nameSpace
     * @param xpath
     * @return
     */
    private XmlCursor getXmlObjectByXPath(XmlObject xml, String nameSpace,
                                          String xpath) {
        XmlCursor xpathCursor = xml.newCursor();
        xpathCursor.toLastChild();
        StringBuilder builder = new StringBuilder();
        Formatter formatter = new Formatter(builder);
        formatter.format("%s %s", nameSpace, xpath);
        xpathCursor.selectPath(formatter.toString());
        formatter.close();
        return xpathCursor;
    }
    
    private void preHandleTable(XWPFDocument document){
        Iterator<XWPFTable> iterator = document.getTablesIterator();
        while (iterator.hasNext()){
            XWPFTable xwpfTable = iterator.next();
            List<XWPFTableRow> xwpfTableRows = xwpfTable.getRows();
            for(XWPFTableRow row : xwpfTableRows){
                List<XWPFTableCell> cells = row.getTableCells();
                for(XWPFTableCell cell : cells){
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    handleParagraph(paragraphs);
                }
            }
        }
    }
    
    private void preHandleParagraphs(XWPFDocument document){
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        this.handleParagraph(paragraphs);
    }
    
    private void handleParagraph(List<XWPFParagraph> paragraphs){
        for(XWPFParagraph paragraph : paragraphs){
            List<XWPFRun> runs = paragraph.getRuns();
            int i = 0;
            for(; i<runs.size() ; i++){
                XWPFRun run = runs.get(i);
                String text = run.getText(0);
                if(HaiweiStringUtil.isNotBlank(text) && isNeededMergeRun(text)){
                    i = WordOperateUtil.mergeRun(text,i,runs);
                }
            }
        }
    }
    
    private boolean isNeededMergeRun(String text){
        Matcher matcher = mergePattern.matcher(text);
        return matcher.find();
    }


}
